# encoding: utf-8
""" 
@version: v1.0 
@author: autumner 
@license: Apache Licence  
@contact: 18322313385@163.com 
@site:  https://gitee.com/autumner/pythoncookbook
@software: PyCharm 
@file: net_xmlrpc_upload_server.py 
@time: 2019/7/24 上午9:33
@note:
基于xmlrpc的文件上传下载文件显示查询-服务端
"""
from xmlrpc.server import SimpleXMLRPCServer, SimpleXMLRPCRequestHandler
from socketserver import ThreadingMixIn
from xmlrpc.client import Binary
from urllib.parse import urlparse
import os
import time
import ssl


# 加入ssl认证
class SSLMixIn:
    def __init__(self, *args, keyfile=None, certfile=None, ca_certs=None,cert_reqs=ssl.CERT_NONE, **kwargs):
        self._keyfile = keyfile
        self._certfile = certfile
        self._ca_certs = ca_certs
        self._cert_reqs = cert_reqs
        super().__init__(*args, **kwargs)

    def get_request(self):
        client, addr = super().get_request()
        client_ssl = ssl.wrap_socket(client,
                                     keyfile=self._keyfile,
                                     certfile=self._certfile,
                                     ca_certs=self._ca_certs,
                                     cert_reqs=self._cert_reqs,
                                     server_side=True)
        return client_ssl,addr



# 设置共享目录
sharedir = 'sharesources'
# 允许地址重用
SimpleXMLRPCServer.allow_reuse_address = True

# Restrict to a particular path.
class RequestHandler(SimpleXMLRPCRequestHandler):
    rpc_paths = ('/RPC2')


# 开启xmlrpc服务多线程
class RPCThreading(ThreadingMixIn, SimpleXMLRPCServer):
    pass

# 开启ssl双向认证服务
class sslXMLRPCServer(SSLMixIn, RPCThreading):
    pass

def get_port(url):
    """
    从URL中提取端口号
    :param url:
    :return:
    """
    name = urlparse(url)[1]
    parts = name.split(':')
    return int(parts[-1])

def getseq():
    '返回日期时间的字符串'
    return time.strftime('%Y%m%d%H%M%s', time.localtime())

def getpostfix(filepath):
    '返回文件名及文件名后缀'
    _, fname = os.path.split(filepath)
    shotname, fix = os.path.splitext(fname)
    return shotname, fix

def checkdir(dir):
    '检查共享目录是否存在，不存在就创建并返回当前路径，存在及返回当前路径'
    if os.path.isdir(dir):
        return os.path.abspath(os.path.join(os.getcwd(), dir))
    else:
        os.mkdir(os.path.abspath(os.path.join(os.getcwd(), dir)))
        return os.path.abspath(os.path.join(os.getcwd(), dir))

def timeStampTotime(timestamp):
    """
    把时间戳转化为时间
    :param timestamp:
    :return:
    """
    timeStruct = time.localtime(timestamp)
    return time.strftime('%Y-%m-%d %H:%M:%S', timeStruct)

def formatSize(bytes):
    """
    字节bytes转换B\KB\M\G
    :param bytes:
    :return:
    """
    try:
        bytes = float(bytes)
    except:
        print("输入的字节格式不对")
        return "input bytes Error"
    i = 0
    units = ["B", "KB", "MB", "GB", "TB"]

    while (abs(bytes) >= 1024):
        bytes = bytes / 1024
        i += 1
        if (i == 4): break
    return f"{round(bytes,2)}{units[i]}"

def get_FileSize(filepath):
    """
    获取文件大小
    :param filepath:
    :return:
    """
    fsize = os.path.getsize(filepath)
    return formatSize(fsize)

def get_fAccessT(filepath):
    """
    获取文件访问时间
    :param filepath:
    :return:
    """
    t = os.path.getatime(filepath)
    return timeStampTotime(t)


class Node:
    '创建上传下载的基类'
    def __init__(self, url, *args, **kwargs):

        self._url = url
        self.KEYFILE = 'server_key.pem'
        self.CERTFILE = 'server_cert.pem'
        self.CA_CERTS = 'client_cert.pem'
        self.s = sslXMLRPCServer(*args, ('', get_port(self._url)), requestHandler=RequestHandler,
                            keyfile=self.KEYFILE,
                            certfile=self.CERTFILE,
                            ca_certs=self.CA_CERTS,
                            cert_reqs=ssl.CERT_NONE,
                            allow_none = True,**kwargs)

    def _start(self):
        '启动服务并注册实例'
        self.s.register_instance(self)
        self.s.serve_forever()

    def upload(self, data, fpath):
        '获取上传内容及客户端上传文件名全路径并把上传内容保存到服务端'
        source_path = checkdir(sharedir)
        shot,ext=getpostfix(fpath)
        file_name = shot + ext
        if (len(file_name) > 128): file_name = getseq() + ext
        with open(os.path.join(source_path, file_name), 'wb') as f:
            f.write(data.data)
            return 1

    def show(self):
        '显示服务端共享文件的列表'
        source_path = checkdir(sharedir)
        lists = []
        for root, dirs, files in os.walk(source_path):
            if files:
                for file in files:
                    lists.append([get_fAccessT(os.path.join(source_path,file)), get_FileSize(os.path.join(source_path,file)), file])
        return lists

    def query(self, query):
        '模糊查找文件'
        queryL = []
        alllist = self.show()
        for l in alllist:
            if ([i for i,x in enumerate(l) if x.find(query) != -1]):
                queryL.append(l)
        return queryL

    def down(self, filename):
        '下载服务器指定文件'
        source_path = checkdir(sharedir)
        filepath = os.path.join(os.path.abspath(source_path), filename)
        if (os.path.isfile(filepath)):
            with open(filepath, 'rb') as f:
                result = Binary(f.read())
                return result
        else:
            return 0


if __name__ == "__main__":
    # serv = Node('http://localhost:18000')
    # print('start service get power on localhost 18000...')
    # serv._start()
    import sys
    if len(sys.argv) != 2:
        print(f"Usage:{sys.argv[0]} http://localhost:port", file=sys.stderr)
        raise SystemExit(1)
    serv = Node(sys.argv[1])
    print(f"start service get power on {sys.argv[1]}")
    try:
        serv._start()
    except:
        print()
        raise SystemExit(1)

'''
Example usage:
Terminal:
python3 net_xmlrpc_upload_server.py http://localhost:18000
'''

